import { IListToTree, ListToTreeReturnProps } from './typing';

type NumberString = number | string;

/**
 * @author Longmo
 * @description list转map 该方法会影响原始对象
 * @param data list数据
 * @param key list中用key值作为map的键
 * @param  F 将list数据中的某些数据，复制一份到该对象中的其他变量 :{oldField:['id','menuName'],field:['key','title']}
 * @param  D 删除的对象数据 一维数组中是string是代表 删除params[key]；一维数组中是数组时 代表删除params[key1][key2] :[id,[xxx,id]] 目前支持两层
 * @returns
 */
export function listToMap<T = Record<NumberString, any>>(
  data: Record<NumberString | symbol, any>[],
  key: NumberString,
  F?: { oldField: NumberString[]; field: NumberString[] } | null,
  D: Array<NumberString | NumberString[]> = ['children'],
): Record<NumberString | symbol, T> {
  const map: Record<string, any> = {};
  data.forEach(async (item) => {
    // 衍生指定对象
    if (F?.field.length === F?.oldField.length) {
      F?.field.forEach((addStr, i) => {
        item[addStr] = item[F.oldField[i]];
      });
    }
    const mapKey = item[key];
    // 删除指定对象
    D?.forEach((d) => {
      if (d instanceof Array) {
        let temporary = item;
        d.forEach((v, i) => {
          if (i !== d.length) {
            temporary = temporary[v];
          }
        });
        delete temporary[d[d.length - 1]];
        return;
      }
      delete item[d];
    });
    // 通过每个对象中的ID生成一个key为ID的map数组
    map[mapKey] = item;
  });

  return map;
}

/**
 * @author Longmo
 * @description tree转map 该方法会影响原始对象
 * @param data tree数据
 * @param key tree中用key值作为map的键
 * @param childrenKey 子元素的key childrenKey = 'children'
 * @param isDelChildren 是否删除 data[children]
 * @returns
 */
export function treeToMap<T = Record<NumberString | symbol, any>>(
  data: Record<NumberString, any>[],
  key: NumberString,
  childrenKey = 'children',
  isDelChildren: boolean = true,
): Record<NumberString | symbol, T> {
  const map: Record<string, any> = {};

  function recu(itemData: Record<string, any>[]) {
    itemData.forEach(async (item) => {
      if (item[childrenKey] && item[childrenKey] instanceof Array) {
        const children = item[childrenKey];
        recu(children);
        // 如果对象中有children则删除
        isDelChildren && delete item[childrenKey];
      }
      // 通过每个对象中的ID生成一个key为ID的map数组
      map[item[key]] = item;
    });
  }

  recu(data);
  return map;
}

/**
 * @author Longmo
 * @description 对象数组转树  兼容多棵树 多个顶点情况，该方法会影响原始对象，如果不想影响原始对象请使用JSON.stringfly复制一份处理
 * @param data 元数据对象数组
 * @param id 对象唯一ID名称
 * @param pid 父ID名称
 * @param children 存储子对象的对象名
 * @param F 衍生对象
 * @param D 删除对象
 * @param vertex 顶级`pid`值 如果该对象没有值会自动计算顶点，可指定顶点
 * @returns `(T & children)[]`
 *
 */
export function listToTree<
  T = Record<NumberString, any>,
  U extends NumberString = 'children',
>({
  data = [],
  id = 'id',
  pid = 'parentId',
  children = 'children',
  F,
  D,
  vertex,
}: IListToTree): ListToTreeReturnProps<T, U>[] {
  if (!data.length) return [];
  const map: Record<string, any> = {};
  const tree: ListToTreeReturnProps<T, U>[] = [];
  data.forEach(async (item, i) => {
    // 如果对象中有children则删除
    item[children] && delete item[children];

    // 菜单请求当前工具类时
    // data[i].icon && (data[i].Icon = getIcon(data[i].icon)); // 根据icon衍生Icon dom对象
    // 衍生新数据
    F?.oldField.forEach((v, k) => {
      item[F?.field[k]] = item[v];
    });
    // 删除数据
    if (D && item?.[D?.key] === D?.value) {
      if (D.self) {
        delete data[i];
      } else {
        D?.field &&
          D?.field.forEach((df) => {
            delete data[i]?.[df];
          });
      }
    }
    // 通过每个对象中的ID生成一个key为ID的map数组
    map[item[id]] = item;
  });
  let keyValueArr: NumberString[] = vertex || [];
  if (!vertex?.length) {
    // 寻找Pid
    for (const item of data) {
      if (!item) {
        continue;
      }
      if (!Object.keys(map).includes(item[pid])) {
        if (!keyValueArr.includes(item[pid])) {
          keyValueArr.push(item[pid]);
        }
      }
    }
  }
  data.forEach((item) => {
    if (!keyValueArr.includes(item[pid])) {
      if (!map[item[pid]]) {
        return;
      }
      map[item[pid]][children] = map[item[pid]]?.[children] || [];
      map[item[pid]][children].push(item);
    } else {
      tree.push(item as ListToTreeReturnProps<T, U>);
    }
  });
  return tree || [];
}
